import {CSSProperties} from 'react';
import {ipcRenderer} from 'electron';
import {Action} from 'redux';

import immutableArray from '../common/immutableArray';
import immutableObject from '../common/immutableObject';

import {preventDeleteChild} from '../views';

import {IDesign} from '../types/IDesign';
import {IView} from '../types/IView';
import {ActionType} from './ActionType';
import isParentView, {IParentView} from '../types/IParentView';

export interface DesignsState {
  [designId: string]: IDesign;
}

export interface DesignsAction extends Action {
  type: ActionType;
  design?: IDesign;
  designId?: string;
  viewId?: string;
  childView?: IView;
  style?: CSSProperties;
  viewAttrs?: object;
}

const viewParent: {
  [viewId: string]: string;
} = {};

const views: {
  [viewId: string]: IParentView | IView;
} = {};

function designReducer(state: DesignsState = {}, action: DesignsAction): DesignsState {
  switch (action.type) {
    case ActionType.ADD_DESIGN:
      return _addDesign(state, action.design);

    case ActionType.REMOVE_DESIGN:
      return _removeDesign(state, action.designId);

    case ActionType.ADD_CHILD_VIEW:
      return _addChildView(state, action.designId, action.viewId, action.childView);

    case ActionType.REMOVE_CHILD_VIEW:
      return _removeChildView(state, action.designId, action.viewId, action.childView);

    case ActionType.REMOVE_VIEW:
      return _removeView(state, action.designId, action.viewId);

    case ActionType.MOVE_VIEW:
      return _moveView(state, action.designId, action.viewId, action.childView);

    case ActionType.UPDATE_VIEW_STYLE:
      return _updateView(state, action.designId, action.viewId, 'style', action.style);

    case ActionType.UPDATE_VIEW_HOVER_STYLE:
      return _updateView(state, action.designId, action.viewId, 'hoverStyle', action.style);

    case ActionType.UPDATE_VIEW_ATTR:
      return _updateView(state, action.designId, action.viewId, 'attrVals', action.viewAttrs);

    default:
      return state;
  }
}

function _addDesign(state: DesignsState, design: IDesign): DesignsState {
  if (!design) {
    return state;
  }

  _onViewAdded(design.root);

  return immutableObject.set(state, design.id, design);
}

function _removeDesign(state: DesignsState, designId: string): DesignsState {
  if (!designId || !state[designId]) {
    return state;
  }

  _onViewRemoved(state[designId].root);

  // 删除指定的设计
  return immutableObject.remove(state, designId);
}

function _doAddChildView(design: IDesign, view: IParentView, childView: IView): IDesign {
  const newView: IParentView = {...view};
  views[newView.id] = newView;

  newView.children = immutableArray.add(newView.children, childView);

  // 要更新一下map 的状态
  _onViewAdded(childView, newView.id);

  // 更新整个父级的组件
  const rootView = _updateViewTree(newView);

  // 更新设计
  return immutableObject.set(design, 'root', rootView);
}

function _addChildView(state: DesignsState, designId: string, viewId: string, childView: IView): DesignsState {
  if (!state[designId]) {
    // 没有这个设计
    return state;
  }

  const view = views[viewId];
  if (!view || !isParentView(view)) {
    // 没有这个组件, 或者组件不能包含子组件
    return state;
  }

  const newDesign = _doAddChildView(state[designId], view, childView);

  // 更新state
  return immutableObject.set(state, designId, newDesign);
}

function _doRemoveChildView(design: IDesign, view: IParentView, childView: IView, childViewIndex: number): IDesign {

  const newView: IParentView = {...view};
  views[newView.id] = newView;

  newView.children = immutableArray.remove(newView.children, childViewIndex);

  // 要更新一下map 的状态
  _onViewRemoved(childView);

  // 更新整个父级的组件
  const rootView = _updateViewTree(newView);

  // 更新设计
  return immutableObject.set(design, 'root', rootView);
}

function _removeChildView(state: DesignsState, designId: string, viewId: string, childView: IView): DesignsState {
  if (!state[designId]) {
    // 没有这个设计
    return state;
  }

  const view = views[viewId];
  if (!view || !isParentView(view)) {
    // 没有这个组件, 或者组件不能包含子组件
    return state;
  }

  const childViewIndex = _findViewIndex(view as IParentView, childView);
  if (childViewIndex === -1) {
    // 没有这个子组件
    return state;
  }

  if (preventDeleteChild(view.viewType, childViewIndex)) {
    // 有些子组件是不能删除的
    return state;
  }

  // 更新设计
  const newDesign = _doRemoveChildView(state[designId], view, childView, childViewIndex);

  // 更新state
  return immutableObject.set(state, designId, newDesign);
}

function _moveView(state: DesignsState, designId: string, viewId: string, childView: IView): DesignsState {
  if (!state[designId]) {
    // 没有这个设计
    return state;
  }

  const oldParentViewId: string = viewParent[childView.id];
  if (oldParentViewId === viewId) {
    // 如果是同一个父组件, 暂时还不支持, 不做处理.
    // TODO 后续应该可以支持调整子组件的位置
    return state;
  }

  const view = views[viewId];
  if (!view || !isParentView(view)) {
    // 没有这个组件, 或者组件不能包含子组件
    return state;
  }

  const oldParentView: IView = views[oldParentViewId];
  if (!oldParentView || !isParentView(oldParentView)) {
    // 没有这个组件, 或者组件不能包含子组件
    return state;
  }

  const childViewIndex = _findViewIndex(oldParentView as IParentView, childView);
  if (childViewIndex === -1) {
    // 没有这个子组件
    return state;
  }

  if (preventDeleteChild(oldParentView.viewType, childViewIndex)) {
    // 有些子组件是不能删除的
    return state;
  }

  // 更新设计
  const newRemovedDesign = _doRemoveChildView(state[designId], oldParentView, childView, childViewIndex);

  const newAddedDesign = _doAddChildView(newRemovedDesign, views[viewId] as IParentView, childView);

  // 更新state
  return immutableObject.set(state, designId, newAddedDesign);
}

function _removeView(state: DesignsState, designId: string, viewId: string): DesignsState {
  if (!state[designId]) {
    // 没有这个设计
    return state;
  }

  const view = views[viewId];
  if (!view || view.id === state[designId].root.id) {
    // 没有这个组件, 或者组件是 设计 的 根组件
    return state;
  }

  const parentViewId = viewParent[viewId];
  return _removeChildView(state, designId, parentViewId, view);
}

function _updateView(state: DesignsState,
                     designId: string,
                     viewId: string,
                     field: 'attrVals' | 'style' | 'hoverStyle',
                     fieldValue?: object): DesignsState {
  if (!state[designId]) {
    // 没有这个设计
    return state;
  }

  const view = views[viewId];
  if (!view) {
    // 没有这个组件
    return state;
  }

  const newView: IView = {...view};
  views[newView.id] = newView;

  if (field === 'style') {
    newView.style = immutableObject.extend(newView.style, fieldValue);

    if (newView.attrVals.className) {
      ipcRenderer.send('update-class', designId, newView.attrVals.className, newView.style);
    }
  }
  if (field === 'hoverStyle') {
    newView.hoverStyle = immutableObject.extend(newView.hoverStyle, fieldValue);

    if (newView.attrVals.className) {
      ipcRenderer.send('update-class', designId, `${newView.attrVals.className}:hover`, newView.hoverStyle);
    }
  }
  if (field === 'attrVals') {
    newView.attrVals = immutableObject.extend(newView.attrVals, fieldValue);

    if (newView.attrVals.className !== view.attrVals.className) {
      ipcRenderer.send('update-class', designId, newView.attrVals.className, newView.style, view.attrVals.className);
    }
  }

  const rootView = _updateViewTree(newView);

  // 更新设计
  const newDesign = immutableObject.set(state[designId], 'root', rootView);

  // 更新state
  return immutableObject.set(state, designId, newDesign);
}

function _updateViewTree(view: IView): IView {
  const parentViewId = viewParent[view.id];
  if (!parentViewId) {
    // 没有父级组件了, 那说明到了根组件了
    return view;
  }

  // 父级组件
  const parentView = views[parentViewId] as IParentView;

  // 查找当前组件在父级组件的位置
  const viewIndex = _findViewIndex(parentView, view);

  // 更新当前组件, 这会产生一个新的父级组件的子组件列表
  const newParentChildren = immutableArray.set(parentView.children, viewIndex, view);

  // 更新父级组件的子组件列表, 这会产生一个新的父级组件
  const newParentView = immutableObject.set(parentView, 'children', newParentChildren);
  views[newParentView.id] = newParentView;

  // 然后依次递归上去, 直到根组件
  return _updateViewTree(newParentView);
}

function _findViewIndex(parentView: IParentView, view: IView): number {
  // 查找组件所在的位置
  return parentView.children.findIndex(childView => {
    return childView.id === view.id;
  });
}

function _onViewAdded(view: IView, parentViewId?: string) {
  views[view.id] = view;

  if (parentViewId) {
    viewParent[view.id] = parentViewId;
  }

  if (isParentView(view)) {
    (view.children || []).forEach(childView => _onViewAdded(childView, view.id));
  }
}

function _onViewRemoved(view: IView) {
  delete views[view.id];
  delete viewParent[view.id];

  if (isParentView(view)) {
    (view.children || []).forEach(childView => _onViewRemoved(childView));
  }
}

export default designReducer;
